#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import os
import shutil
import sys

execfile(os.path.join(sys.path[0], "framework.py"))

from oriolus.docker.storages.memorystorage import MemoryStorage
from oriolus.gaffer.cache import Cache
from oriolus.gaffer.gaffer.selectstrategy import LatestStrategy
from oriolus.gaffer.gaffer.catalogue import Catalogue
from oriolus.gaffer.gaffer import Gaffer
from oriolus.pith.packets.meta import Meta
from oriolus.pith.packets.packet import Packet, Status
from pmock import *
from unittest import TestCase, TestLoader, TextTestRunner

class TestInstallConfiguration(TestCase):
    def setUp(self):
        self.__test_packets_path = os.path.join(".", "testdata", "testpackets")

        self.__cache_path = os.path.join(".", "testdata", "gaffer_cache")
        self.__temp_dirs = (self.__cache_path, )

        self.__setup_mocks()

    def tearDown(self):
        for path in self.__temp_dirs:
            if os.path.exists(path):
                shutil.rmtree(path)

    def test_install_configuration01(self):
        self.__memory_storage.add(Packet(Meta("packet_a", "1.2.3.4", None, [], [], []), status=Status.INSTALLED))
        self.__memory_storage.add(Packet(Meta("packet_b", "2.3.4.5", None, [], [], []), status=Status.INSTALLED))
        self.__memory_storage.add(Packet(Meta("packet_c", "3.4.5.6", None, [], [], []), status=Status.INSTALLED))

        packet_folder = "test_install01"
        packet_name_a = "packet_a-1.2.3.4"
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        trotter_packet_list = [packet_name_a,]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        self.__mock_trotter.stubs().get_versions(eq("packet_a")).will(return_value(["1.2.3.4"]))        

        configuration = ("packet_a-1.2.3.4", "packet_b-2.3.4.5", "packet_c-3.4.5.6")
        
        self.__mock_docker.expects(once()).search(status=eq(Status.INSTALLED)).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED), Packet(Meta('packet_b', '2.3.4.5', None, [], [], []), status=Status.INSTALLED), Packet(Meta('packet_c', '3.4.5.6', None, [], [], []), status=Status.INSTALLED)]))
        self.__mock_docker.expects(never()).search(name=eq('packet_a')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED)]))

        self.__gaffer.install_configuration(configuration)
        self.__mock_docker.verify()
        
    def test_install_configuration02(self):
        self.__memory_storage.add(Packet(Meta("packet_c", "3.4.5.6", None, [], [], []), status=Status.INSTALLED))
        self.__memory_storage.add(Packet(Meta("packet_b", "2.3.4.5", None, [], [], []), status=Status.INSTALLED))

        packet_folder = "test_install01"
        packet_name_a = "packet_a-1.2.3.4"
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        trotter_packet_list = [packet_name_a,]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        self.__mock_trotter.stubs().get_versions(eq("packet_a")).will(return_value(["1.2.3.4"]))        
        self.__mock_trotter.expects(once_or_never()).download(eq("packet_a"), eq("1.2.3.4")).will(return_value(packet_path_a))

        configuration = ("packet_a-1.2.3.4", "packet_b-2.3.4.5", "packet_c-3.4.5.6")
        
        expected_docker_actions = (
                                   "search(status=eq(Status.INSTALLED)).will(return_value([Packet(Meta('packet_b', '2.3.4.5', None, [], [], []), status=Status.INSTALLED), Packet(Meta('packet_c', '3.4.5.6', None, [], [], []), status=Status.INSTALLED)]))",
                                   "search(full_name=eq('packet_a-1.2.3.4')).will(return_value([]))",
                                   "search(full_name=eq('packet_a-1.2.3.4')).will(return_value([]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.4'), status=eq(Status.INSTALLING), base_path=eq(None))"
                                   )

        self.assert_install_configuration(configuration, expected_docker_actions)     
        
    def test_install_configuration03(self):
        packet_a = Packet(Meta("packet_a", "1.2.3.4", None, [], [], []), status=Status.INSTALLED)
        packet_b= Packet(Meta("packet_c", "3.4.5.6", None, [], [], []), status=Status.INSTALLED)
        packet_c = Packet(Meta("packet_b", "2.3.4.5", None, [], [], []), status=Status.INSTALLED)
        
        packet_list = [packet_a, packet_b, packet_c]
        
        self.__memory_storage.add_range(packet_list)

        packet_folder = "test_install01"
        packet_name_a = "packet_a-1.2.3.4"
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        trotter_packet_list = [packet_name_a,]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        configuration = ("packet_c-3.4.5.6", "packet_b-2.3.4.5")
        
        self.__mock_docker.expects(at_least_once()).search(get_all=eq(True)).will(return_value(packet_list))
        
        expected_docker_actions = (
                                   "search(status=eq(Status.INSTALLED)).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED), Packet(Meta('packet_b', '2.3.4.5', None, [], [], []), status=Status.INSTALLED), Packet(Meta('packet_c', '3.4.5.6', None, [], [], []), status=Status.INSTALLED)]))",
                                   "search(name=eq('packet_a')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED)]))",
                                   "remove(eq('packet_a-1.2.3.4'))"
                                   )

        self.assert_install_configuration(configuration, expected_docker_actions)   
        
    def test_install_configuration04(self):
        packet_a = Packet(Meta("packet_a", "1.2.3.4", None, [], [], []), status=Status.INSTALLED_AS_DEPENDENCY)
        packet_b = Packet(Meta("packet_b", "2.3.4.5", None, [], [], [{"Name": "packet_a", "Version": "1.2.3.4"}]), status=Status.INSTALLED)
        packet_c = Packet(Meta("packet_c", "3.4.5.6", None, [], [], []), status=Status.INSTALLED)
        
        packet_list = [packet_a, packet_b, packet_c]
        
        self.__memory_storage.add_range(packet_list)
        
        packet_folder = "test_install_configuration04"
        packet_name_a = "packet_a-1.2.3.4"
        packet_path_a = self.__get_packet_path(packet_folder, packet_name_a)
        packet_name_d = "packet_d-4.5.6.7"
        packet_path_d = self.__get_packet_path(packet_folder, packet_name_d)
        trotter_packet_list = [packet_name_a, packet_name_d]
        self.__mock_trotter.stubs().get_packet_list().will(return_value(trotter_packet_list))
        
        configuration = ("packet_c-3.4.5.6", "packet_d-4.5.6.7", "packet_a-1.2.3.4")
        
        self.__mock_trotter.stubs().get_versions(eq("packet_a")).will(return_value(["1.2.3.4"]))        
        self.__mock_trotter.expects(once()).download(eq("packet_a"), eq("1.2.3.4")).will(return_value(packet_path_a))
        self.__mock_trotter.stubs().get_versions(eq("packet_d")).will(return_value(["4.5.6.7"]))        
        self.__mock_trotter.expects(once()).download(eq("packet_d"), eq("4.5.6.7")).will(return_value(packet_path_d))
        
        self.__mock_docker.expects(at_least_once()).search(get_all=eq(True)).will(return_value(packet_list))        
        
        expected_docker_actions = (
                                   "search(status=eq(Status.INSTALLED)).will(return_value([Packet(Meta('packet_b', '2.3.4.5', None, [], [], [{'Name': 'packet_a', 'Version': '1.2.3.4'}]), status=Status.INSTALLED), Packet(Meta('packet_c', '3.4.5.6', None, [], [], []), status=Status.INSTALLED)]))",
                                   
                                   "search(name=eq('packet_b')).will(return_value([Packet(Meta('packet_b', '2.3.4.5', None, [], [], [{'Name': 'packet_a', 'Version': '1.2.3.4'}]))]))",
                                   "search(name=eq('packet_a')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED)]))",
                                   "search(name=eq('packet_a')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED)]))",
                                   "remove(eq('packet_b-2.3.4.5'))",
                                   "remove(eq('packet_a-1.2.3.4'))",
                                                                      
                                   "search(full_name=eq('packet_d-4.5.6.7')).will(return_value([]))",
                                   "search(full_name=eq('packet_a-1.2.3.4')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED_AS_DEPENDENCY)]))",
                                   "search(full_name=eq('packet_a-1.2.3.4')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED_AS_DEPENDENCY)]))",
                                   
                                   "search(full_name=eq('packet_d-4.5.6.7')).will(return_value([]))",
                                   "search(full_name=eq('packet_a-1.2.3.4')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED_AS_DEPENDENCY)]))",
                                   "install(packet_path=string_contains('packet_d-4.5.6.7'), status=eq(Status.INSTALLING), base_path=eq(None))",
                                   
                                   "search(full_name=eq('packet_a-1.2.3.4')).will(return_value([Packet(Meta('packet_a', '1.2.3.4', None, [], [], []), status=Status.INSTALLED_AS_DEPENDENCY)]))",
                                   "install(packet_path=string_contains('packet_a-1.2.3.4'), status=eq(Status.INSTALLING), base_path=eq(None))",
                                   )
            
        self.assert_install_configuration(configuration, expected_docker_actions)
        
        entries = self.__memory_storage.entries    
        self.assertEqual(3, len(entries))  
                
    def __setup_mocks(self):
        cache = Cache(self.__cache_path)
        self.__mock_docker = Mock()
        self.__mock_trotter = Mock()
        catalogue = Catalogue(self.__mock_trotter)
        
        select_strategy = LatestStrategy()
        self.__gaffer = Gaffer(cache, self.__mock_docker, self.__mock_trotter, catalogue, select_strategy)
        
        self.__memory_storage = MemoryStorage()
        self.__mock_docker.stubs().extract_storage().will(return_value(self.__memory_storage))

        self.__mock_virtual_docker = None
        self.__original_mock_virtual_docker_install = None
        self.__exceptional_packet_path = None

    def assert_install_configuration(self, configuration, expected_docker_actions):
        mock_docker = self.__mock_docker

        for i in range(len(expected_docker_actions)):
            action = expected_docker_actions[i]

            if i == 0:
                eval("mock_docker.expects(once()).%s.id(i)" % action)
            else:
                eval("mock_docker.expects(once()).%s.id(i).after(i-1)" % action)

        self.__gaffer.install_configuration(configuration)
        self.__mock_docker.verify()
        
    def __get_packet_path(self, packet_folder, packet_name):
        return os.path.join(self.__test_packets_path, packet_folder, packet_name + ".zip.ori")


if __name__ == "__main__":
    suite = TestLoader().loadTestsFromTestCase(TestInstallConfiguration)
    TextTestRunner(verbosity=2).run(suite)